﻿#include <string_view>
#include <string>
#include <cerrno>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <vector>
#include "qqgame.h"

static bool LoadCommonArgs(int argc, char const* argv[], std::string& openid, std::string& openkey, std::string& pfkey,
                           uint16_t& port) {
    if (argc != 5) return false;
    for (int j = 1; j < 5; ++j) {
        std::string_view p(argv[j]);
        auto idx = p.find('=');
        if (idx != std::string::npos) {
            std::string_view key = p.substr(0, idx);
            std::string_view value = p.substr(idx + 1);
            if (key == "id") {
                openid = value;
            } else if (key == "key") {
                openkey = value;
            } else if (key == "pfkey") {
                pfkey = value;
            } else if (key == "port") {
                port = uint16_t(std::stoul(std::string(value)));
            } else {
                return false;
            }
        }
    }
    return true;
}

static bool LoadExtraArgs(const char* exe_path, std::string& gameid, unsigned int& appid) {
    std::filesystem::path p(exe_path);
    p = p.parent_path();
    p.append("extra_qqgame_test_params.txt");
    if (!std::filesystem::is_regular_file(p)) return false;

    // 读取文件的前 n 个字节
    std::ifstream fs(p);
    if (!fs.is_open() || !fs.good()) {
        return false;
    }
    char buf[1024] = {0};
    fs.read(buf, sizeof(buf));
    std::string_view data(buf);
    if (data.empty()) return false;

    // 读取按照空格分隔
    static const auto ReadNum = [](const std::string_view& data, const char* prefix) {
        auto i = data.find(prefix);
        if (i == std::string_view::npos) return std::string_view("");
        i += strlen(prefix);
        auto j = i;
        while (j < data.size()) {
            if (!std::isdigit(data[j])) break;
            ++j;
        }
        if (i == j) return std::string_view("");
        return data.substr(i, j - i);
    };

    gameid = ReadNum(data, "gameid=");
    if (gameid.empty()) return false;
    std::string appid_str(ReadNum(data, "appid="));
    if (appid_str.empty()) return false;
    appid = unsigned int(std::stoul(appid_str));

    return true;
}

struct Action {
    const char* cmd;
    const char* help;
    std::function<bool(QQGame* qqgame, const std::vector<std::string>& params)> handler;
};

static const Action actions[] = {
    {"exit", "exit app",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         qqgame->ExitGame();
         return true;
     }},
    {"ls", "show configuration",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         printf("  valid: %d\n", qqgame->Valid());
         printf("  sandbox: %d\n", qqgame->sandbox);
         printf("  auto_exit_game: %d\n", qqgame->auto_exit_game);
         printf("  openid: %s\n", qqgame->openid());
         printf("  openkey: %s\n", qqgame->openkey());
         printf("  pfkey: %s\n", qqgame->pfkey());
         printf("  gameid: %s\n", qqgame->gameid());
         printf("  appid: %u\n", qqgame->appid());
         return true;
     }},
    {"sandbox", "toggle sandbox state",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         qqgame->sandbox = !qqgame->sandbox;
         return true;
     }},
    {"autoexit", "toggle auto_exit_game state",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         qqgame->auto_exit_game = !qqgame->auto_exit_game;
         return true;
     }},
    {"pfkey", "<new key> update pfkey",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         qqgame->set_pfkey(params[1].c_str());
         return true;
     }},
    {"open", "<title> <url>open a web page",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         if (params.size() != 3) return false;
         qqgame->OpenWeb(params[1].c_str(), params[2].c_str(), 800, 600);
         return true;
     }},
    {"pay", "<url> open buy goods window",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         if (params.size() != 2) return false;
         qqgame->Pay(params[1].c_str());
         return true;
     }},
    {"vip", "open blue-vip charge window",
     [](QQGame* qqgame, const std::vector<std::string>& params) {
         qqgame->BuyVip();
         return true;
     }},
};

static void Help() {
    printf("invalid cmd !\n");
    printf("valid cmds:\n");
    for (auto& a : actions) {
        printf("    [%s] %s\n", a.cmd, a.help);
    }
    printf("\n");
}

static std::vector<std::string> ParseCmd(const char* buf) {
    std::vector<std::string> ret = {};
    if (!buf) return ret;
    auto l = strlen(buf);

    size_t head = 0, tail = 0;
    char c;
    while (tail < l) {
        c = buf[tail];
        if (c <= 0x20) {
            if (head < tail) ret.push_back(std::string(buf + head, tail - head));
            head = tail + 1;
        }
        ++tail;
    }
    if (head < tail) ret.push_back(std::string(buf + head, tail - head));
    return ret;
}

void Process() {
    char buf[1024] = {0};
    printf("enter cmd> ");
    gets_s(buf, sizeof(buf) - 1);
    auto vals = ParseCmd(buf);
    if (vals.empty()) {
        return Help();
    }
    const auto& cmd = vals[0];
    auto p = QQGame::Instance();
    bool show_help = true;
    for (auto& a : actions) {
        if (cmd == a.cmd) {
            show_help = !a.handler(p, vals);
            break;
        }
    }
    if (show_help) Help();
}

int main(int argc, char const* argv[]) {
    // argv 需要传递标准的大厅启动参数 id=xxx key=xxx pfkey=xxx port=xxx
    // 然后需要在当前程序所在目录放置一个附加参数文件 extra_qqgame_test_params.txt
    // 内容如下: gameid=xxx appid=xxx

    // 加载参数
    std::string openid, openkey, pfkey;
    uint16_t port;
    if (!LoadCommonArgs(argc, argv, openid, openkey, pfkey, port)) {
        fprintf(stderr, "Invalid start params\n");
        return EINVAL;
    }

    std::string gameid;
    unsigned int appid;
    if (!LoadExtraArgs(argv[0], gameid, appid)) {
        fprintf(stderr, "Failed to load extra start params\n");
        return EINVAL;
    }

    // 构建对象
    auto qqgame = QQGame::Instance();
    qqgame->AddObserver([](QQGame::MessageType msg) {
        switch (msg) {
            case QQGame::MessageType::kHideGame:
                printf("receive msg [kHideGame]\n");
                return;
            case QQGame::MessageType::kShowGame:
                printf("receive msg [kShowGame]\n");
                return;
            case QQGame::MessageType::kBringToTop:
                printf("receive msg [kBringToTop]\n");
                return;
            case QQGame::MessageType::kPayClose:
                printf("receive msg [kPayClose]\n");
                return;
            case QQGame::MessageType::kBuyVipClose:
                printf("receive msg [kBuyVipClose]\n");
                return;
            case QQGame::MessageType::kWebClose:
                printf("receive msg [kWebClose]\n");
                return;
            case QQGame::MessageType::kLoseConnection:
                printf("receive msg [kLoseConnection]\n");
                exit(0);
                return;
            case QQGame::MessageType::kExitGame:
                printf("receive msg [kExitGame]\n");
                exit(0);
                return;
            default:
                return;
        }
    });
    qqgame->Init(gameid.c_str(), appid, openid.c_str(), openkey.c_str(), pfkey.c_str(), port);
    if (!qqgame->Valid()) {
        fprintf(stderr, "Failed to connect QQGame-hall\n");
        return -1;
    }

    while (1) {
        Process();
    }

    return 0;
}
